package cn.shujuhai.cms.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.shujuhai.cms.domain.*;
import cn.shujuhai.cms.mapper.SysChannelMapper;
import cn.shujuhai.cms.service.InfoArticleService;
import cn.shujuhai.cms.service.LogChannelService;
import cn.shujuhai.cms.service.SysChannelService;
import cn.shujuhai.cms.service.thymeleaf.StaticHtmlService;
import cn.shujuhai.cms.util.SysChannelConverter;
import cn.shujuhai.common.domain.SysRole;
import cn.shujuhai.common.domain.SysRoleChannel;
import cn.shujuhai.common.domain.SysRoleInfo;
import cn.shujuhai.common.mapper.SysUserMapper;
import cn.shujuhai.common.pluging.data.ResultCode;
import cn.shujuhai.common.pluging.data.ResultVo;
import cn.shujuhai.common.service.SysRoleChannelService;
import cn.shujuhai.common.service.SysRoleInfoService;
import cn.shujuhai.common.service.SysRoleService;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author: zhangDeZhong
 * @Date: 2024/12/5 12:01
 * @Description: 针对表【sys_channel(栏目)】的数据库操作Service实现
 **/
@Service
@RequiredArgsConstructor
public class SysChannelServiceImpl extends ServiceImpl<SysChannelMapper, SysChannel>
        implements SysChannelService {

    private final SysUserMapper sysUserMapper;
    private final SysRoleChannelService sysRoleChannelService;
    private final SysRoleInfoService sysRoleInfoService;
    private final SysRoleService sysRoleService;
    private final InfoArticleService infoArticleService;
    private final StaticHtmlService staticHtmlService;
    private final LogChannelService logChannelService;

    //权限代码列表
    private static final List<String> codeList = Arrays.asList("channel_admin", "info_admin", "super_admin");

    /**
     * 获取父级栏目树
     *
     * @return 父级栏目树响应结果
     */
    @Override
    public List<Map<String, Object>> getParentMenusAsTree() {
        List<Long> channelIds = this.sysUserMapper.selectChannelIdByUserId(StpUtil.getLoginIdAsLong());
        var wrapper = new MPJLambdaWrapper<SysChannel>();
        wrapper.selectAll(SysChannel.class)
                .select(SysTemplateType::getCode)
                .leftJoin(SysTemplate.class, SysTemplate::getTemplateId, SysChannel::getTemplateId)
                .leftJoin(SysTemplateType.class, SysTemplateType::getTemplateTypeId, SysTemplate::getType)
                .in(SysChannel::getChannelId, channelIds);
        List<SysChannel> allChannels = this.list(wrapper);
        List<SysChannel> sortedChannelList = allChannels.stream()
                .sorted(Comparator.comparingLong(SysChannel::getParentId)  // 按父菜单排序
                        .thenComparing(SysChannel::getChannelSort))  // 按排序字段排序
                .collect(Collectors.toList());
        return buildMenuTree(sortedChannelList, 0);
    }

    /**
     * 递归获取栏目树
     *
     * @param allMenus 栏目列表
     * @param parentId 父栏目ID
     * @return 栏目树信息
     */
    private List<Map<String, Object>> buildMenuTree(List<SysChannel> allMenus, long parentId) {
        List<Map<String, Object>> tree = new ArrayList<>();
        // 筛选出当前父级的所有子菜单，并按照 sort 字段排序
        List<SysChannel> childrenChannels = allMenus.stream()
                .filter(channel -> channel.getParentId() == parentId)
                .sorted(Comparator.comparing(SysChannel::getChannelSort))  // 按照 sort 字段排序
                .toList();
        for (SysChannel channel : childrenChannels) {
            if (channel.getParentId() == parentId) {
                Map<String, Object> node = new HashMap<>();
                node.put("id", channel.getChannelId());
                node.put("title", channel.getChannelName());
                node.put("code", channel.getCode());
                node.put("children", buildMenuTree(allMenus, channel.getChannelId())); // 递归处理子菜单
                tree.add(node);
            }
        }
        return tree;
    }

    /**
     * 根据栏目ID获取栏目信息
     *
     * @param channelId 栏目ID
     * @return 栏目信息结果
     */
    @Override
    public SysChannel getChannelById(Integer channelId) {
        var wrapper = new MPJLambdaWrapper<SysChannel>();
        wrapper.selectAll(SysChannel.class)  // 获取 SysChannel 表的所有字段
                .select(SysTemplate::getTemplateName)  // 获取模板的名称
                .select(SysTemplate::getType)  //获取模板类型
                .selectAs("parentMenu", SysChannel::getChannelName, SysChannel::getParentName)  // 获取父栏目名称
                .leftJoin(SysTemplate.class, SysTemplate::getTemplateId, SysChannel::getTemplateId)  // 左连接模板表
                .leftJoin(SysChannel.class, "parentMenu", SysChannel::getChannelId, SysChannel::getParentId)  // 左连接父栏目表
                .eq(SysChannel::getChannelId, channelId);  // 根据栏目ID进行筛选
        return this.getOne(wrapper);
    }

    /**
     * 更新栏目信息
     *
     * @param sysChannel 需要栏目信息
     * @return 结果信息
     */
    @Override
    public ResultVo<?> updateSaveChannel(SysChannel sysChannel) {
        SysChannel OriginSysChannel = this.getById(sysChannel.getChannelId());
        boolean checkInfoExists = checkInfoExists(List.of(sysChannel.getChannelId()));
        if (checkInfoExists) {
            sysChannel.setUrlKey(null);
            sysChannel.setTemplateId(null);
            sysChannel.setDetailTemplate(null);
            sysChannel.setParentId(null);
        } else {
//            sysChannel.setChannelName(HtmlUtils.htmlEscape(sysChannel.getChannelName()));
//            sysChannel.setUrlKey(HtmlUtils.htmlEscape(sysChannel.getUrlKey()));
        }
        sysChannel.setCount(null);
        if ((!OriginSysChannel.getChannelName().equals(sysChannel.getChannelName())) && verifyChannelName(sysChannel.getChannelName())) {
            return ResultVo.failure("栏目名称已存在！");
        }
        if ((!OriginSysChannel.getUrlKey().equals(sysChannel.getUrlKey())) && verifyChannelUrl(sysChannel.getUrlKey())) {
            return ResultVo.failure("栏目路径已存在！");
        }

        boolean updated = this.updateById(sysChannel);
        if (updated) {
            this.staticHtmlService.initChannelStaticHtml();
            return ResultVo.success();
        }
        return ResultVo.failure(ResultCode.FAILED);
    }

    /**
     * 添加栏目信息
     *
     * @param sysChannel 需要添加的栏目信息
     * @return 结果信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultVo<?> addChannel(SysChannel sysChannel) {
        if (verifyChannelName(sysChannel.getChannelName())) {
            return ResultVo.failure("栏目名称已存在！");
        }
        if (verifyChannelUrl(sysChannel.getUrlKey())) {
            return ResultVo.failure("栏目路径已存在！");
        }
        sysChannel.setCount(null);
//        sysChannel.setChannelName(HtmlUtils.htmlEscape(sysChannel.getChannelName()));
//        sysChannel.setUrlKey(HtmlUtils.htmlEscape(sysChannel.getUrlKey()));
        boolean save = this.save(sysChannel);
        if (save) {
            // 添加栏目权限
            List<SysRole> roleList = this.sysRoleService.list(Wrappers.<SysRole>lambdaQuery().in(SysRole::getRoleCode, codeList));
            List<SysRoleChannel> sysRoleChannelList = new ArrayList<>();
            List<SysRoleInfo> sysRoleInfoList = new ArrayList<>();
            for (SysRole role : roleList) {
                SysRoleChannel sysRoleChannel = new SysRoleChannel();
                sysRoleChannel.setRoleId(role.getRoleId());
                sysRoleChannel.setChannelId(sysChannel.getChannelId());
                sysRoleChannelList.add(sysRoleChannel);

                SysRoleInfo sysRoleInfo = new SysRoleInfo();
                sysRoleInfo.setRoleId(role.getRoleId());
                sysRoleInfo.setInfoId(sysChannel.getChannelId());
                sysRoleInfoList.add(sysRoleInfo);
            }
            boolean savedBatch = this.sysRoleChannelService.saveBatch(sysRoleChannelList);
            boolean saveBatch = this.sysRoleInfoService.saveBatch(sysRoleInfoList);
            if (savedBatch && saveBatch) {
                this.staticHtmlService.initChannelStaticHtml();
                return ResultVo.success();
            }
        }
        return ResultVo.failure(ResultCode.FAILED);
    }

    /**
     * 验证栏目名称是否存在
     *
     * @param channelName 栏目名称
     * @return 如果栏目名称存在，则返回 true；否则返回 false
     */
    public boolean verifyChannelName(String channelName) {
        return this.getOne(Wrappers.<SysChannel>lambdaQuery().eq(SysChannel::getChannelName, channelName)) != null;
    }

    /**
     * 验证栏目路径是否存在
     *
     * @param urlKey 栏目路径
     * @return 如果栏目路径存在，则返回 true；否则返回 false
     */
    public boolean verifyChannelUrl(String urlKey) {
        return this.getOne(Wrappers.<SysChannel>lambdaQuery().eq(SysChannel::getUrlKey, urlKey)) != null;
    }


    /**
     * 删除栏目信息
     *
     * @param channelId 需要删除的栏目信息
     * @return 结果信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultVo<?> deleteChannel(Long channelId) {
        if (channelId == 1 || channelId == 33) {
            return ResultVo.failure("系统栏目不可删除！");
        }
        List<Long> childChannelIds = new ArrayList<>();
        findAllChildChannels(channelId, childChannelIds);
        childChannelIds.add(channelId);
        boolean checkInfoExists = checkInfoExists(childChannelIds);
        if (checkInfoExists) {
            return ResultVo.failure("该栏目下还存在文章信息，请清空后重试！");
        }
        boolean removeRoleChannel = this.sysRoleChannelService.remove(new LambdaQueryWrapper<SysRoleChannel>()
                .in(SysRoleChannel::getChannelId, childChannelIds));
        boolean removeRoleInfo = this.sysRoleInfoService.remove(new LambdaQueryWrapper<SysRoleInfo>()
                .in(SysRoleInfo::getInfoId, childChannelIds));
        if (removeRoleChannel || removeRoleInfo) {
            boolean removeChannels = this.removeByIds(childChannelIds);
            if (removeChannels) {
                this.staticHtmlService.initChannelStaticHtml();
                return ResultVo.success();
            }
        }
        return ResultVo.failure(ResultCode.FAILED);
    }

    /**
     * 检查栏目列表下是否存在文章信息
     *
     * @param channelIds 栏目ID列表
     * @return 如果栏目列表下存在文章信息，则返回true，否则返回false
     */
    public boolean checkInfoExists(List<Long> channelIds) {
        return !this.infoArticleService.list(Wrappers.<InfoArticle>lambdaQuery().in(InfoArticle::getChannelId, channelIds)).isEmpty();
    }

    /**
     * 根据栏目ID获取栏目路径
     *
     * @param channelId 栏目ID
     * @return 栏目路径结果信息
     */
    @Override
    public ResultVo<?> getChannelUrlById(Long channelId) {
        String urlKey = this.getById(channelId).getUrlKey();
        return ResultVo.success(urlKey);
    }


    /**
     * 递归查找当前栏目ID下所有的子栏目
     *
     * @param parentId        父栏目ID
     * @param childChannelIds 子栏目ID集合
     */
    private void findAllChildChannels(Long parentId, List<Long> childChannelIds) {
        // 查询当前父栏目下的所有子栏目
        List<SysChannel> childChannels = this.list(
                Wrappers.<SysChannel>lambdaQuery().eq(SysChannel::getParentId, parentId)
        );

        // 将当前父栏目下的所有子栏目ID添加到列表中
        for (SysChannel childChannel : childChannels) {
            childChannelIds.add(childChannel.getChannelId());
            // 递归查找子栏目
            findAllChildChannels(childChannel.getChannelId(), childChannelIds);
        }
    }


    /**
     * 获取栏目信息列表
     *
     * @return 栏目信息列表结果
     */
    @Override
    public ResultVo<?> getChannelList() {
        List<SysChannel> list = this.list();
        return ResultVo.success(list);
    }


    /**
     * 构建栏目菜单树
     *
     * @param channelIds 栏目ID列表
     * @param href       栏目菜单路径
     * @param module     类型
     * @return 栏目树列表
     */
    @Override
    public List<Map<String, Object>> buildChannelMenuTree(List<Long> channelIds, String href, String module) {
        List<SysChannel> sysChannelList = this.listByIds(channelIds);
        // 先根据父菜单和排序字段对所有菜单进行排序
        List<SysChannel> sortedChannelList = sysChannelList.stream()
                .sorted(Comparator.comparingLong(SysChannel::getParentId)  // 按父菜单排序
                        .thenComparing(SysChannel::getChannelSort))  // 按排序字段排序
                .collect(Collectors.toList());
        // 0 表示顶级父级栏目
        return buildChannelMenuTree(sortedChannelList, 0, href, module);
    }

    /**
     * 递归构建栏目菜单树
     *
     * @param sysChannelList 栏目列表
     * @param parentId       父栏目ID
     * @param href           菜单路径
     * @param module         类型
     * @return 栏目树列表
     */
    private List<Map<String, Object>> buildChannelMenuTree(List<SysChannel> sysChannelList,
                                                           long parentId, String href, String module) {
        List<Map<String, Object>> tree = new ArrayList<>();

        // 筛选出当前父级的所有子菜单，并按照 sort 字段排序
        List<SysChannel> childrenChannels = sysChannelList.stream()
                .filter(channel -> channel.getParentId() == parentId)
                .sorted(Comparator.comparing(SysChannel::getChannelSort))  // 按照 sort 字段排序
                .toList();

        // 遍历子菜单并递归生成树
        for (SysChannel channel : childrenChannels) {
            if (channel.getParentId() == parentId) {
                Map<String, Object> node = new HashMap<>();
                if (module.equals("info")) {
                    node.put("id", "info" + channel.getChannelId());
                    node.put("href", href + "?infoId=" + channel.getChannelId());
                }
                if (module.equals("channel")) {
                    node.put("id", "channel" + channel.getChannelId());
                    node.put("href", href + "?channelId=" + channel.getChannelId());
                }
                node.put("title", channel.getChannelName());

                // 递归处理子菜单
                List<Map<String, Object>> children = buildChannelMenuTree(sysChannelList, channel.getChannelId(), href, module);
                node.put("children", children);

                // 根据是否有子数据动态设置 type
                node.put("type", children.isEmpty() ? 1L : 2L);

                tree.add(node);
            }
        }
        return tree;
    }

    /**
     * 获得栏目浏览量总和
     *
     * @return 文章浏览量信息结果
     */
    @Override
    public ResultVo<?> getTotalViewCount() {
        // 通过流处理将查询结果转换为Integer类型，并计算总和
        Integer totalViewCount = this.baseMapper.selectObjs(Wrappers.<SysChannel>lambdaQuery().select(SysChannel::getCount))
                .stream()
                .mapToInt(count -> Math.toIntExact((Long) count))
                .sum();
        // 返回成功结果，包含总浏览量
        return ResultVo.success(totalViewCount);

    }

    /**
     * 获取每个栏目及其文章的浏览量
     *
     * @return 结果信息
     */
    @Override
    public ResultVo<?> getChannelAndArticleViews() {
        List<Map<String, Object>> views = this.baseMapper.selectChannelAndArticleViews();
        return ResultVo.success(views);
    }

    /**
     * 获取栏目模板代码
     *
     * @param channelId 栏目ID
     * @return 栏目模板代码结果信息
     */
    @Override
    public ResultVo<?> getTemplateCode(Long channelId) {
        var wrapper = new MPJLambdaWrapper<SysChannel>();
        wrapper.select(SysChannel::getIsChecked)
                .select(SysChannel::getChannelId)
                .select(SysChannel::getChannelName)
                .select(SysTemplateType::getCode)
                .leftJoin(SysTemplate.class, SysTemplate::getTemplateId, SysChannel::getTemplateId)
                .leftJoin(SysTemplateType.class, SysTemplateType::getTemplateTypeId, SysTemplate::getType)
                .eq(SysChannel::getChannelId, channelId);
        SysChannel sysChannel = this.getOne(wrapper);
        if (sysChannel == null) {
            return ResultVo.failure("栏目信息有误");
        }
        return ResultVo.success(sysChannel);
    }

    /**
     * 根据栏目ID判断栏目下是否有文章
     *
     * @return 父级栏目树响应结果
     */
    @Override
    public ResultVo<?> judgeInfoExists(Long channelId) {
        boolean checkInfoExists = checkInfoExists(List.of(channelId));
        return ResultVo.success(checkInfoExists);
    }

    @Override
    public void countChannelNum(Map<String, String> body) {
        String pageUrl = body.get("pageUrl");
        String zhChannel = "/channel/zh-CN/";
        String enChannel = "/channel/en/";
        String k = "";
        if (pageUrl.contains(zhChannel)) {
            k = zhChannel;
        } else if (pageUrl.contains(enChannel)) {
            k = enChannel;
        }
        if (k.isEmpty()) {
            return;
        }
        String urlKey = pageUrl.split(k)[1].split(".html")[0];

        if (urlKey.contains("/")) {
            urlKey = urlKey.split("/")[0];
        }
        this.update(Wrappers.<SysChannel>lambdaUpdate()
                .eq(SysChannel::getUrlKey, urlKey)
                .setSql("count = count + 1"));


    }

    @Override
    public ResultVo<?> viewArticleRanking() {
        List<SysChannel> list = this.list(Wrappers.<SysChannel>lambdaQuery()
                .select(SysChannel::getChannelName, SysChannel::getCount)
                .orderByDesc(SysChannel::getCount)
                .orderByDesc(SysChannel::getCreateTime));
        return ResultVo.success(list);
    }

    @Override
    public void exportSysChannel(HttpServletResponse response) throws IOException {
        try {
            // 1. 查询数据
            List<SysChannel> channels = this.list();

            // 2. 转换为导出DTO
            List<SysChannelExportDTO> exportData = channels.stream()
                    .map(SysChannelConverter::toExportDTO)
                    .collect(Collectors.toList());
            // 3. 设置响应头


            String fileName = URLEncoder.encode("栏目数据导出", StandardCharsets.UTF_8).replaceAll("\\+", "%20");
            response.setContentType("application/octet-stream;charset=utf-8");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");

            // 4. 使用EasyExcel导出
            EasyExcel.write(response.getOutputStream()).head(SysChannelExportDTO.class)
                    .excelType(ExcelTypeEnum.XLSX)
                    .sheet("栏目数据")
                    .doWrite(exportData);
        } catch (Exception e) {
            e.printStackTrace();
            // 清理响应流，避免后续操作
            response.reset();
            // 返回错误信息
            response.setContentType("text/plain;charset=utf-8");
            try {
                response.getWriter().write("导出失败：" + e.getMessage());
            } catch (IOException ioException) {
                ioException.printStackTrace();
            }
        }

    }

    @Override
    public ResultVo<?> getCountChannel(String date) {
        if (date == null || date.isEmpty()) {
            // 如果没有传递日期范围，从 sys_channel 表中查询访问量前10的栏目
            List<SysChannel> list = this.list(new QueryWrapper<SysChannel>().orderByDesc("count").last("LIMIT 10"));
            return ResultVo.success(list);
        } else {
            // 解析日期范围
            String[] dates = date.split(" - ");
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            LocalDateTime startDate = LocalDateTime.parse(dates[0], formatter);
            LocalDateTime endDate = LocalDateTime.parse(dates[1], formatter);

            // 根据日期范围筛选并统计每个栏目的访问量
            QueryWrapper<LogChannel> queryWrapper = new QueryWrapper<>();
            queryWrapper.ge("systime", startDate)
                    .le("systime", endDate);
            List<LogChannel> logChannels = this.logChannelService.list(queryWrapper);

            // 使用 Java 8 的流（Stream）来统计每个栏目的访问次数
            List<ChannelVisitStatistics> statisticsList = logChannels.stream()
                    .collect(Collectors.groupingBy(
                            LogChannel::getChannelId,
                            Collectors.summingInt(logChannel -> 1)
                    )).entrySet().stream().map(entry -> {
                        ChannelVisitStatistics statistics = new ChannelVisitStatistics();
                        statistics.setChannelId(entry.getKey());
                        statistics.setChannelName(this.getChannelNameById(entry.getKey()));
                        statistics.setCount(entry.getValue());
                        return statistics;
                    }).sorted((s1, s2) -> s2.getCount().compareTo(s1.getCount())) // 按访问量降序排序
                    .limit(10) // 限制返回的数量为10个
                    .collect(Collectors.toList());

            return ResultVo.success(statisticsList);
        }
    }

    public String getChannelNameById(Long channelId) {
        SysChannel sysChannel = this.getOne(new QueryWrapper<SysChannel>().lambda().select(SysChannel::getChannelName).eq(SysChannel::getChannelId, channelId
        ));
        return sysChannel.getChannelName();
    }
}